package ldh.im.fxbase.richtext;

import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;
import javafx.scene.input.Clipboard;
import javafx.scene.input.DataFormat;
import ldh.im.ImConfig;
import ldh.im.fxbase.richtext.item.*;
import ldh.im.fxbase.util.*;
import ldh.im.util.ConfigUtil;
import ldh.im.util.LocalSeqUtil;
import ldh.rpc.cache.CacheService;
import org.fxmisc.richtext.GenericStyledArea;
import org.fxmisc.richtext.StyledTextArea;
import org.fxmisc.richtext.model.Codec;
import org.fxmisc.richtext.model.ReadOnlyStyledDocument;
import org.fxmisc.richtext.model.SegmentOps;
import org.fxmisc.richtext.model.TextOps;
import org.reactfx.util.Either;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

public class ImTextArea extends GenericStyledArea<Void, Either<String, TextItem>, String> {

    private static final TextOps<String, String> STYLED_TEXT_OPS = SegmentOps.styledTextOps();
    private static final TextItemOps<String> HYPERLINK_OPS = new TextItemOps<>();

    private static final TextOps<Either<String, TextItem>, String> EITHER_OPS = STYLED_TEXT_OPS._or(HYPERLINK_OPS, (s1, s2) -> Optional.empty());

    public static final Integer MAX_LENGTH = 2000;
    public static final DataFormat IM_DATA_FORMAT = new DataFormat("IM_RICHTEXT");

    public ImTextArea() {
        super(
                null,
                (t, p) -> { },
                "",
                EITHER_OPS,
                e -> e.getSegment().unify(
                        text -> StyledTextArea.createStyledTextNode(text, null, (t, style) -> {}),
                        textItem -> textItem.createNode()
                )
        );

        setWrapText(true);
        getStyleClass().add("im-text-area");
        setStyleCodecs(TextItem.createVoidCodec(), Codec.styledSegmentCodec(Codec.eitherCodec(TextItem.createStringCodec(), TextItem.createCodec()), TextItem.createStringCodec()));
        this.textProperty().addListener(e->{
            if (ImTextArea.this.getText().length() > MAX_LENGTH) {
                DialogUtil.alert("文本超长", "填入的内容太长");
            }
        });
    }

    public String getData() {
        String[] array = getParagraphs().stream().map(ps->{
            StringBuilder stringBuilder = new StringBuilder();
            ps.getSegments().forEach(s->{
                if (s.isLeft()) {
                    stringBuilder.append(s.getLeft());
                } else {
                    TextItem textItem = s.getRight();
                    stringBuilder.append(textItem.getImData());
                }
            });
            return stringBuilder.toString();
        }).toArray(n -> new String[n]);
        return String.join("\n", array);
    }

    public void insertImage(String imageUrl) {
        insertTextItem(new ImageTextItem(imageUrl));
    }

    public void insertEmoji(String emoji) {
        insertTextItem(new EmojiTextItem(emoji));
    }

    public void insertString(String str) {
        replaceSelection(str);
    }

    public void insertTextItem(TextItem textItem) {
        ReadOnlyStyledDocument<Void, Either<String, TextItem>, String> ros =
                ReadOnlyStyledDocument.fromSegment(Either.right(textItem), null, textItem.getStyle(), getSegOps());
        replaceSelection(ros);
    }

    public void clear() {
        this.replaceText(0, this.getContent().length(), "");
    }

    public void setImData(String content) {
        clear();
        appendImData(content);
    }

    public void appendImData(String content) {
        List<Object> objects = ImRichTextUtil.split(content);
        for(Object obj : objects) {
            if (obj instanceof TextItem) {
                TextItem textItem = (TextItem) obj;
                insertTextItem(textItem);
            } else {
                String s = obj.toString();
                if (s.equals("")) continue;
                insertString(s);
            }
        }
    }

    @Override
    public void paste() {
        Clipboard clipboard = Clipboard.getSystemClipboard();
        try {

            if (clipboard.hasString()) {
                String str = clipboard.getString();
                if (str.length() + this.getData().length() > MAX_LENGTH) {
                    DialogUtil.alert("文本超长", "填入的内容太长");
                    return;
                }

                if(str.contains(TextItem.SPLIT_STRING)) {
                    this.appendImData(str);
                    return;
                }
            }

            if (clipboard.hasImage()) {
                Image image = clipboard.getImage();
                if (image != null) {
                    if (image.getUrl() == null) {
                        saveCopyImage(image);
                    } else {
                        insertImage(image.getUrl());
                    }
                }
            }

            if (clipboard.hasFiles()) {
                List<File> files = clipboard.getFiles();
                for(File file : files) {
                    if (FileUtil.isImage(file)) {
                        insertImage(file.getPath());
                    } else if (FileUtil.isAudio(file)) {
                        AudioTextItem fileTextItem = new AudioTextItem(file);
                        insertTextItem(fileTextItem);
                    } else if (FileUtil.isVideo(file)) {
                        VideoTextItem fileTextItem = new VideoTextItem(file);
                        insertTextItem(fileTextItem);
                    } else {
                        FileTextItem fileTextItem = new FileTextItem(file);
                        insertTextItem(fileTextItem);
                    }
                }
                return;
            }

            Object obj = clipboard.getContent(IM_DATA_FORMAT);
            if (obj != null) {
                this.appendImData(obj.toString());
                return;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        super.paste();
    }

    public boolean isTooLength(String str) {
        if (str.length() > MAX_LENGTH) {
            return true;
        }
        return false;
    }

    private void saveCopyImage(Image image) {
        Task<String> saveImageTask = new Task() {

            @Override
            protected Object call() throws Exception {
                BufferedImage bImage = SwingFXUtils.fromFXImage(image, null);
                CacheService cacheService = RpcUtil.getRpcBootstrap().getRpcConfig().getCacheService();
                String userId = (String) cacheService.getCache(ImConfig.LOGIN_USER_ID);
                String fileName = LocalSeqUtil.createLocalSeq();
                String fileDir = ConfigUtil.getRoot() + ConfigUtil.buildFileDir("copy", new Date(), userId);
                if (Files.notExists(Paths.get(fileDir))) {
                    Files.createDirectories(Paths.get(fileDir));
                }
                String imageFile = fileDir + File.separator + ImConfig.IM_NAME + "_" + fileName + ".png";
                try {
                    ImageIO.write(bImage, "png", new File(imageFile));
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                return imageFile;
            }
        };
        ThreadPoolUtil.submitTask(saveImageTask);
        saveImageTask.setOnSucceeded(s->{
            insertImage(saveImageTask.getValue());
        });
    }
}
